package xyz.lilei123.demo.service.impl;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.bulk.BulkProcessor;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import xyz.lilei123.demo.entity.Position;
import xyz.lilei123.demo.index.constants.IndexEnum;
import xyz.lilei123.demo.repository.PositionRepository;
import xyz.lilei123.demo.service.IPositionService;

import javax.annotation.Resource;
import javax.persistence.criteria.Predicate;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author jack
 * @version 1.0
 * @date 25/01/2022 12:26
 **/
@Service
@Slf4j
public class PositionServiceImpl implements IPositionService {

    @Resource
    private PositionRepository positionRepository;

    @Resource
    private BulkProcessor esBulkProcessor;

    @Resource
    private RestHighLevelClient client;

    private static final String POSITION_INDEX = "position";

    @Override
    public List<Map<String, Object>> searchPosition(String keyword) {
        List<Map<String, Object>> list = new ArrayList<>();
        MatchQueryBuilder positionName = QueryBuilders.matchQuery("positionName", keyword);
        SearchResponse searchResponse = search(positionName, 5, 1);
        SearchHit[] hits = searchResponse.getHits().getHits();
        for (SearchHit hit : hits) {
            list.add(hit.getSourceAsMap());
        }
        if (list.size() >= 5) {
            return list;
        }
        appendAdvantage(list, positionName);
        return list;
    }

    private void appendAdvantage(List<Map<String, Object>> list, MatchQueryBuilder positionName) {
        SearchResponse searchResponse;
        SearchHit[] hits;
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.mustNot(positionName);
        boolQueryBuilder.must(QueryBuilders.matchQuery("positionAdvantage", "美女多 员工福利好"));
        searchResponse = search(boolQueryBuilder, 5-list.size(), 1);
        hits = searchResponse.getHits().getHits();
        for (SearchHit hit : hits) {
            list.add(hit.getSourceAsMap());
        }
    }

    private SearchResponse search(QueryBuilder queryBuilder, Integer pageSize, Integer pageNum) {
        SearchRequest searchRequest = new SearchRequest(IndexEnum.POSITION.getIndexName());
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.size(pageSize);
        searchSourceBuilder.from((pageNum-1)*pageSize);
        searchSourceBuilder.query(queryBuilder);
        searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
        // 执行搜索
        searchRequest.source(searchSourceBuilder);
        try {
            return client.search(searchRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            log.error("IOException: ", e);
            throw new RuntimeException(e.getMessage());
        }
    }

    @Override
    public void build(Date start, Date end) {
        List<Position> all = positionRepository.findAll(where(start, end));
        all.forEach(position -> esBulkProcessor.add(new IndexRequest(POSITION_INDEX).source(JSON.parseObject(JSON.toJSONString(position)).getInnerMap())));
        esBulkProcessor.flush();
    }


    private Specification<Position> where(Date start, Date end) {
        return (Specification<Position>) (root, query, cb) -> {
            List<Predicate> list = new ArrayList<Predicate>();
            if (start!=null){
                list.add(cb.greaterThanOrEqualTo(root.get("createTime").as(Date.class), start));
            }
            if (end!=null){
                list.add(cb.lessThanOrEqualTo(root.get("createTime").as(Date.class), end));
            }
            return query.where(list.toArray(new Predicate[0])).getRestriction();
        };
    }

}
